package gupao.concurrency.demo.risk.calc.processor;

import gupao.concurrency.demo.risk.model.NodeTree;
import gupao.concurrency.demo.risk.model.RiskCapitalCalcContext;
import gupao.concurrency.demo.risk.model.Node;
import gupao.concurrency.demo.risk.calc.simulator.CalcSimulator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

@Slf4j
@Component("levelConcurrentCalcProcessor")
public class LevelConcurrentCalcProcessorImpl implements CalcProcessor {

    @Autowired
    private CalcThreadPoolService calcThreadPoolService;

    @Override
    public void calculateRiskCapital(RiskCapitalCalcContext context) {
        NodeTree tree = context.getDeptTree();
        ExecutorService executorService = calcThreadPoolService.getLevelNodeThreadPool();
        long start = System.currentTimeMillis();
        for( int i = tree.getMinLevel(); i  <= tree.getMaxLevel(); i++ ){
            List<NodeCalcTask> tasks = generateLevelNodesTask(context, i);
            infoLog("Start to calculate level " + i + " nodes: total number = " + tasks.size());
            long start2 = System.currentTimeMillis();
            List<Future<?>> futureList = new ArrayList<>(tasks.size());
            for (NodeCalcTask task : tasks) {
                futureList.add(executorService.submit(task));
            }
            for (Future<?> f : futureList) {
                try {
                    f.get();
                } catch (Exception e) {
                    context.setLastError(e);
                }
            }
            infoLog("...Done. time cost:" + (System.currentTimeMillis()-start2) + "ms");
        }
        infoLog("Completed. Total time cost:" + (System.currentTimeMillis()-start) + "ms");
    }

    List<NodeCalcTask> generateLevelNodesTask(RiskCapitalCalcContext context, int level){
        List<Node> levelNodeList = context.getDeptTree().getLevelMap().get(level);
        List<NodeCalcTask> tasks = new ArrayList<>(levelNodeList.size());
        for (Node node : levelNodeList) {
            tasks.add(new NodeCalcTask(node, context));
        }
        return tasks;
    }

    static class NodeCalcTask implements Runnable{

        final Node node;
        final RiskCapitalCalcContext context;
        public NodeCalcTask(Node node, RiskCapitalCalcContext context) {
            this.node = node;
            this.context = context;
        }

        @Override
        public void run() {
            CalcSimulator.calcNodeQaf(node, context.getDeptTree());
            CalcSimulator.calcRiskCaptital(node, context.getDeptTree());
        }
    }

    void infoLog(String msg){
        log.info(msg);
    }

}
